﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud

#if NO_SILVERLIGHT

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

#if USE_HYPER
using Hyper.ComponentModel;
#endif

namespace BrainTechLLC
{
    public static class Serialization
    {
        #region Methods

        /// <summary>
        /// Deserializes an object from a memory stream using binary serialization
        /// </summary>
        /// <param name="ms"></param>
        /// <returns></returns>
        public static object DeserializeFromBinary(this MemoryStream ms)
        {
            return GetFormatter().Deserialize(ms);
        }

        /// <summary>
        /// Deserializes an object from a memory stream using binary serialization and a specified formatter
        /// </summary>
        /// <param name="ms"></param>
        /// <param name="bf"></param>
        /// <returns></returns>
        public static object DeserializeFromBinary(this MemoryStream ms, IFormatter bf)
        {
            return bf.Deserialize(ms);
        }

        /// <summary>
        /// Deserializes an object from a file stream using binary serialization
        /// </summary>
        /// <param name="ms"></param>
        /// <returns></returns>
        public static object DeserializeFromBinary(this FileStream ms)
        {
            return GetFormatter().Deserialize(ms);
        }

#if USE_ZIP
        public static byte[] SerializeToZippedBinary(object o)
        {
            byte[] bytes;
            using (MemoryStream ms = new MemoryStream())
            {
                GetFormatter().Serialize(ms, o);
                bytes = ms.ToArray();
            }
            using (MemoryStream ms = new MemoryStream())
            {
                Stream stream = new DeflaterOutputStream(ms,
                new Deflater(Deflater.BEST_COMPRESSION), bytes.Length);
                try
                {
                    stream.Write(bytes, 0, bytes.Length);
                }
                finally
                {
                    stream.Close();
                    stream.Dispose();
                }
                return ms.ToArray();
            }
        }

        public static object DeserializeFromZippedBinary(this byte[] byteData)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                byte[] data = new byte[32000];
                Stream s = new InflaterInputStream(new MemoryStream(byteData));
                try
                {
                    while (true)
                    {
                        int bytes = s.Read(data, 0, 32000);
                        if (bytes < 1) break;
                        ms.Write(data, 0, bytes);
                    }
                }
                finally
                {
                    s.Close();
                    s.Dispose();
                }
                return ms.DeserializeFromBinary();
            }
        }
#endif

        /// <summary>
        /// Deserializes an object from a compressed memory stream
        /// </summary>
        /// <param name="ms"></param>
        /// <returns></returns>
        public static object DeserializeFromCompressedBinary(this MemoryStream ms)
        {
            using (MemoryStream m = new MemoryStream(ms.GetBuffer().Decompress()))
            {
                m.Seek(0, SeekOrigin.Begin);
                return m.DeserializeFromBinary();
            }
        }

        /// <summary>
        /// Serializes an object from a compressed memory stream and outputs time taken to serialize the object
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public static byte[] SerializeToCompressedBinaryWithTiming(this object o)
        {
            byte[] data;
            using (MemoryStream ms = new MemoryStream())
            {
                try
                {
                    Stopwatch watch = new Stopwatch();
                    watch.Start();
                    GetFormatter().Serialize(ms, o);
                    watch.Stop();
                    Console.WriteLine(watch.ElapsedMilliseconds);
                }
                catch { throw; }
                data = ms.ToArray();
            }
            return data.Compress();
        }

        /// <summary>
        /// Deserializes an object from a compressed byte array, optionally re-throwing if a serialization exception is thrown or decompression failed
        /// </summary>
        /// <param name="data"></param>
        /// <param name="throwIfException"></param>
        /// <returns></returns>
        public static object DeserializeFromCompressedBinary(this byte[] data, bool throwIfException)
        {            
            byte[] decompressed;

            // decompress the data
            try
            {
                decompressed = data.Decompress();
            }
            catch
            {
                if (throwIfException)
                {
                    throw;
                }

                return null;
            }

            return DeserializeFromBinary(decompressed, throwIfException);            
        }

        /// <summary>
        /// Deserializes an object from a byte array, optionally re-throwing if a serialization exception is thrown
        /// </summary>
        /// <param name="data"></param>
        /// <param name="throwIfException"></param>
        /// <returns></returns>
        public static object DeserializeFromBinary(this byte[] data, bool throwIfException)
        {
            object o = null;

            using (MemoryStream ms = new MemoryStream(data))
            {
                ms.Seek(0, SeekOrigin.Begin);
                try
                {
                    o = ms.DeserializeFromBinary();
                }
                catch
                {
                    if (throwIfException)
                    {
                        throw;
                    }

                    o = null;
                }
                ms.Close();
            }
            return o;
        }

        /// <summary>
        /// Deserializes an object from a compressed file stream, optionally re-throwing if a serialization exception is thrown or decompression failed
        /// </summary>
        /// <param name="fs"></param>
        /// <returns></returns>
        public static object DeserializeFromCompressedBinary(this FileStream fs, bool throwIfException)
        {
            byte[] data;
            long len = fs.Length;

            using (BinaryReader br = new BinaryReader(fs))
            {
                data = br.ReadBytes(Convert.ToInt32(len));
            }

            return data.DeserializeFromCompressedBinary(throwIfException);
        }

        /// <summary>
        /// Reads a compressed binary serialized file containing an array of object of type T and returns a list of the deserialized objects
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dir"></param>
        /// <returns></returns>
        public static List<T> ReadStorage<T>(this string dir)
            where T : class
        {
            List<T> results = new List<T>();
            string file = typeof(T).Name;

            if (File.Exists(file))
            {
                using (FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read))
                {
                    Stopwatch sw = new Stopwatch();
                    sw.Start();
                    try
                    {
                        object o = fs.DeserializeFromCompressedBinary(true);
                        sw.Stop();
                        Console.WriteLine(sw.ElapsedMilliseconds);

                        return new List<T>((T[])o);
                    }
                    catch { throw; }
                }
            }

            return results;
        }

        /// <summary>
        /// Save a compressed binary serialized file containing an array of object of type T
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dir"></param>
        /// <param name="results"></param>
        public static void SaveStorage<T>(this string dir, List<T> results)
            where T : class
        {
            string file = typeof(T).Name;

            T[] arrayResults = results.ToArray();
            byte[] bytes = arrayResults.SerializeToCompressedBinary(true);
            using (FileStream fs = new FileStream(file, FileMode.Create, FileAccess.ReadWrite))
            {
                using (BinaryWriter bw = new BinaryWriter(fs))
                {
                    bw.Write(bytes);
                    bw.Close();
                }
                fs.Close();
            }
        }

        /// <summary>
        /// Serializes an object to a byte array
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public static byte[] SerializeToBinary(this object o)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                GetFormatter().Serialize(ms, o);
                return ms.ToArray();
            }
        }

        /// <summary>
        /// Serializes an object to the specified memory stream
        /// </summary>
        /// <param name="o"></param>
        /// <param name="ms"></param>
        public static void SerializeToBinaryMemoryStream(this object o, MemoryStream ms)
        {
            GetFormatter().Serialize(ms, o);
        }

        /// <summary>
        /// Serializes and object to a compressed binary byte array, optionally throwing an exception if an error occurs during serialization or during compression
        /// </summary>
        /// <param name="o"></param>
        /// <param name="throwIfException"></param>
        /// <returns></returns>
        public static byte[] SerializeToCompressedBinary(this object o, bool throwIfException)
        {
            byte[] data;

            using (MemoryStream ms = new MemoryStream())
            {
                try
                {
                    GetFormatter().Serialize(ms, o);
                }
                catch
                {
                    if (throwIfException)
                    {
                        throw;
                    }
                }

                data = ms.ToArray();
                return data.Compress();
            }
        }

        /// <summary>
        /// Serializes an object, compresses it, and places it in the specified memory stream
        /// </summary>
        /// <param name="o"></param>
        /// <param name="ms"></param>
        /// <param name="throwIfException"></param>
        public static void SerializeToCompressedBinaryMemoryStream(this object o, MemoryStream ms, bool throwIfException)
        {
            byte[] data = o.SerializeToCompressedBinary(throwIfException);
            ms.Write(data, 0, data.Length);
        }

        private static IFormatter GetFormatter()
        {
            //return new NxBinaryFormatter();
            return new BinaryFormatter();
        }

        #endregion Methods
    }
}

#else

#endif